﻿// -----------------------------------------------------------------------
// <copyright file="ConsoleManager.cs" company="FastNET Services">
//     Copyright (c) 2008-2009. All Rights Reserved.
// </copyright>
// <license>
//     GPLv2
// </license>
// <warranty>
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//     BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </warranty>
// -----------------------------------------------------------------------
#region Usings

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using GateShift.Servers.GameServer.HelperClasses;
using GateShift.Servers.GameServer.LoggingSystem;
using GateShift.Services.SDK.Services.HostIntegration;

#endregion

namespace GateShift.Servers.GameServer.Managers.Consoles
{
    /// <summary>
    /// Manager class for handling the console interface into the server
    /// </summary>
    internal class ConsoleManager : IManager, ICommandLineInterfaceHandler
    {
        #region Constant Variables
        // These variables are used to define the placement for the logs
        private const byte LOG_FIELDLENGTH_TIME = 7;
        private const byte LOG_FIELDLENGTH_EVENTID = 10;
        private const byte LOG_FIELDLENGTH_TITLE = 20;
        private const byte LOG_FIELDLENGTH_MESSAGE = 40;
        private const byte ROW_LOGS_TITLE = 6;

        #endregion

        #region ReadOnly Variables

        private readonly Queue<string> _recentCommands;
        private readonly Queue<ConsoleUpdateType> _updates;
        private readonly Dictionary<string, ICommandLineInterfaceHandler> _cliHandlers;
        private readonly Queue<LogMessage> _eventLog;

        private static readonly byte _consoleWidth;
        private static readonly byte _consoleHeight;
        private static readonly object _syncCLI;
        private static readonly int _logCountMaximum;

        #endregion

        #region Private Variables

        private bool _isUpdating;

        private Thread _consoleReadThread;
        private string _currentCommand;

        private int _currentCommandIndex;
        private int _currentCursorIndex;
        private bool _initialized;

        #endregion

        #region CTor/DTor

        /// <summary>
        /// Initializes static members of the ConsoleManager class
        /// </summary>
        static ConsoleManager()
        {
            // We want to limit the size somewhat so we can show more or less information
            if (Console.LargestWindowWidth != 0)
            {
                _consoleWidth = Convert.ToByte(Console.LargestWindowWidth > 150 ? 150 : Console.LargestWindowWidth - 5);
            }
            else
            {
                _consoleWidth = 150;
            }

            if (Console.LargestWindowHeight != 0)
            {
                _consoleHeight = Convert.ToByte(Console.LargestWindowHeight > 60 ? 60 : Console.LargestWindowHeight - 5);
            }
            else
            {
                _consoleHeight = 60;
            }

            _syncCLI = new object();

            _logCountMaximum = _consoleHeight - 5 - ROW_LOGS_TITLE;
        }

        /// <summary>
        /// Initializes a new instance of the ConsoleManager class
        /// </summary>
        public ConsoleManager()
        {
            // Initialize local variables
            _updates = new Queue<ConsoleUpdateType>();
            _cliHandlers = new Dictionary<string, ICommandLineInterfaceHandler>();
            _recentCommands = new Queue<string>();
            _currentCommand = string.Empty;
            _isUpdating = false;
            _eventLog = new Queue<LogMessage>();
            _initialized = false;
        }

        #endregion

        #region Events

        /// <summary>
        /// Event fired when an exit command is issued on the command line
        /// </summary>
        public event EventHandler ExitRequested;

        #endregion

        /// <summary>
        /// Gets a value indicating whether or not the internal threads are running for maintaining the console system
        /// </summary>
        public bool IsAlive
        {
            get
            {
                return _consoleReadThread != null && _consoleReadThread.IsAlive;
            }
        }

        #region Logging

        /// <summary>
        /// Adds a log message to the console screen
        /// </summary>
        /// <param name="eventLog">LogMessage to add</param>
        public void AddEventLog(LogMessage eventLog)
        {
            lock (_eventLog)
            {
                while (_eventLog.Count >= _logCountMaximum)
                {
                    _eventLog.Dequeue();
                }

                _eventLog.Enqueue(eventLog);

                AddConsoleUpdate(ConsoleUpdateType.Content);
            }
        }

        #endregion

        #region Screen Updates

        /// <summary>
        /// Tells the CLI to update a specific part of the screen and begins the refresh process if the process is not already occuring
        /// </summary>
        /// <param name="UpdateType">ConsoleUpdateType with the type of update to process</param>
        public void AddConsoleUpdate(ConsoleUpdateType UpdateType)
        {
            _updates.Enqueue(UpdateType);

            // NOTE: Does this need to be locked because of a possible outside thread calling this?
            if (!_isUpdating)
            {
                UpdateConsole();
            }
        }

        /// <summary>
        /// Queues up all updates and refreshes the screen
        /// </summary>
        public void Refresh()
        {
            Console.Clear();

            Log.AddEntry("Refreshing Screen", string.Empty, LogPriorities.Verbose, TraceEventType.Verbose, (int)CoreLogCategories.ConsoleManager);

            _updates.Enqueue(ConsoleUpdateType.Header);
            _updates.Enqueue(ConsoleUpdateType.Content);
            _updates.Enqueue(ConsoleUpdateType.Command);

            // NOTE: Does this need to be locked because of a possible outside thread calling this?
            if (!_isUpdating)
            {
                UpdateConsole();
            }
        }

        #endregion

        #region System Interfacing

        /// <summary>
        /// Register a command handler to accept input from the command line
        /// </summary>
        /// <param name="rootCommand">Root command for the handler to be triggered</param>
        /// <param name="handler">ICommandLineInterfaceHandler to add</param>
        public void AddCLIHandler(string rootCommand, ICommandLineInterfaceHandler handler)
        {
            if (handler == null)
            {
                throw new ArgumentNullException("handler");
            }

            if (string.IsNullOrEmpty(rootCommand))
            {
                throw new ArgumentNullException("rootCommand", "RootCommand cannot be null or empty");
            }

            // Lock for thread safety while we update the list
            lock (_syncCLI)
            {
                if (!_cliHandlers.ContainsKey(rootCommand.ToLower()))
                {
                    _cliHandlers.Add(rootCommand.ToLower(), handler);
                }
            }
        }

        #endregion

        #region ICommandLineInterfaceHandler Members

        /// <summary>
        /// ICommandLineInterfaceHandler handle command. This is used for processing feedback from the command line
        /// </summary>
        /// <param name="arguments">List of arguments to process</param>
        /// <returns>Returns whether or not the command was successfully processed</returns>
        bool ICommandLineInterfaceHandler.HandleCommand(List<string> arguments)
        {
            if (arguments == null)
            {
                throw new ArgumentNullException("arguments");
            }

            // If there is nothing to process let the requestor know
            if (arguments.Count <= 0)
            {
                return false;
            }

            switch (arguments[0].ToLower())
            {
                case "exit":
                    OnExitRequested();
                    break;

                case "refresh":
                    Refresh();
                    break;

                // If there is nothing to process let the requestor know
                default:
                    return false;
            }

            // Since we defaulted to false already, anything matching in the case statement would end up here
            return true;
        }

        #endregion

        #region IManager Members

        /// <summary>
        /// Starts the managed threads for maintaining the console system
        /// </summary>
        public void Start()
        {
            if (IsAlive)
            {
                throw new InvalidOperationException("Stop must be called before restarting");
            }

            _consoleReadThread = new Thread(ConsoleReadManager)
                                     {
                                         Name = "Console Input Manager",
                                         Priority = ThreadPriority.BelowNormal
                                     };

            Log.AddEntry("Starting Manager", "ConsoleManager", LogPriorities.Informative, TraceEventType.Start, (int)CoreLogCategories.ConsoleManager);

            _consoleReadThread.Start();
        }

        /// <summary>
        /// Stops the managed threads for maintaining the console system
        /// </summary>
        public void Stop()
        {
            if (IsAlive)
            {
                Log.AddEntry("Stopping Manager", "ConsoleManager", LogPriorities.Informative, TraceEventType.Stop, (int)CoreLogCategories.ConsoleManager);
                _consoleReadThread.Abort();
                //// _consoleReadThread.Join(10000); // DO NOT USE THIS. CAUSES EXIT TO HANG FOR CONSOLE MANAGER
            }

            _consoleReadThread = null;
        }

        /// <summary>
        /// Initializes the manager in preparation for starting the threads
        /// </summary>
        public void Initialize()
        {
            Log.AddEntry("Initializing Manager", "ConsoleManager", LogPriorities.Informative, TraceEventType.Verbose, (int)CoreLogCategories.ConsoleManager);

            // We want to set the console styles here
            Console.SetWindowSize(_consoleWidth, _consoleHeight);
            //// Console.SetBufferSize(_consoleWidth, _consoleHeight - 1);

            Console.Title = string.Format("{0} ({1})", SystemGlobals.AssemblyInfo.Title, SystemGlobals.AssemblyInfo.AssemblyVersion);

            // We want to accept Control C because we want to decide how we exit
            Console.TreatControlCAsInput = true;

            // Clear existing screen
            Console.Clear();

            _initialized = true;

            Refresh();
        }

        #endregion

        #region EventRaisers

        /// <summary>
        /// Event Raiser for ExitRequested
        /// </summary>
        protected void OnExitRequested()
        {
            if (ExitRequested != null)
            {
                ExitRequested(this, EventArgs.Empty);
            }
        }

        #endregion

        #region General Handle Commands

        /// <summary>
        /// Creates a log entry and shows an error on the screen that the command entered is invalid
        /// </summary>
        /// <param name="command">command that was originally entered</param>
        private static void ProcessInvalidCommand(string command)
        {
            if (command == null)
            {
                throw new ArgumentNullException("command");
            }

            Log.AddEntry("Invalid Command", command, LogPriorities.Notice, TraceEventType.Warning, (int)CoreLogCategories.ConsoleManager);
        }

        #endregion

        #region Console Formatting

        /// <summary>
        /// Draws a divider on the screen with + symbols on each side for a header
        /// </summary>
        private static void WriteHeaderDivider()
        {
            Console.Write("+");
            Console.Write(string.Empty.PadRight(_consoleWidth - 2, '-'));
            Console.Write("+");
        }

        /// <summary>
        /// Draws centered text on the screen with | symbols on each side as a border
        /// </summary>
        /// <param name="Text">Text to draw centered. Text too long for the screen will be trimmed</param>
        private static void WriteHeaderTextCenter(string Text)
        {
            if (Text == null)
            {
                throw new ArgumentNullException("Text");
            }

            if (Text.Length > (_consoleWidth - 4))
            {
                Text = Text.Remove(_consoleWidth - 4);
            }

            byte consoleHalf = Convert.ToByte((_consoleWidth / 2) - 2);
            byte consoleRemainder = Convert.ToByte(_consoleWidth % 2);

            byte textHalf = Convert.ToByte(Text.Length / 2);
            byte textRemainder = Convert.ToByte(Text.Length % 2);

            // Write text
            Console.Write("| ");
            Console.Write(string.Empty.PadRight(consoleHalf - textHalf));
            Console.Write(Text);
            Console.Write(string.Empty.PadRight((consoleHalf + consoleRemainder) - (textHalf + textRemainder)));
            Console.Write(" |");
        }

        /// <summary>
        /// Draws 2 columns of Key/Values with | symbols on each side as a border
        /// </summary>
        /// <param name="Title1">Key #1 to show. Text too long to display will be trimmed.</param>
        /// <param name="Value1">Value #1 to show. Text too long to display will be trimmed.</param>
        /// <param name="Title2">Key #2 to show. Text too long to display will be trimmed.</param>
        /// <param name="Value2">Value #2 to show. Text too long to display will be trimmed.</param>
        private static void WriteHeader2Column(string Title1, string Value1, string Title2, string Value2)
        {
            if (Title1 == null)
            {
                throw new ArgumentNullException("Title1");
            }

            if (Value1 == null)
            {
                throw new ArgumentNullException("Value1");
            }

            if (Title2 == null)
            {
                throw new ArgumentNullException("Title2");
            }

            if (Value2 == null)
            {
                throw new ArgumentNullException("Value2");
            }

            // Calculate halfs and stuff
            byte consoleHalf = Convert.ToByte((_consoleWidth / 2) - 3);
            byte consoleRemainder = Convert.ToByte(_consoleWidth % 2);
            byte fieldHalf = Convert.ToByte((consoleHalf / 2) - 2);

            // Trim titles to fit
            if (Title1.Length > fieldHalf)
            {
                Title1 = Title1.Remove(fieldHalf);
            }

            if (Title2.Length > fieldHalf)
            {
                Title2 = Title2.Remove(fieldHalf);
            }

            // Trim values to fit
            if (Value1.Length > fieldHalf)
            {
                Value1 = Value1.Remove(fieldHalf);
            }

            if (Value2.Length > fieldHalf)
            {
                Value2 = Value2.Remove(fieldHalf);
            }

            // Write column 1
            Console.Write("| ");
            Console.Write(string.Empty.PadRight(fieldHalf - Title1.Length));
            Console.Write(Title1);
            Console.Write(" : ");
            Console.Write(Value1);
            Console.Write(string.Empty.PadRight(fieldHalf - Value1.Length));

            Console.Write("    ");

            // Write column 2
            Console.Write(string.Empty.PadRight(fieldHalf - Title1.Length));
            Console.Write(Title2);
            Console.Write(" : ");
            Console.Write(Value2);
            Console.Write(string.Empty.PadRight(fieldHalf - Value2.Length));

            Console.Write(string.Empty.PadRight(consoleRemainder));
            Console.Write(" |");
        }

        //// This section can be uncommented as needed when a single column is needed
        //// private static void WriteHeader1Column(string Title, string Value)
        //// {
        ////    if (Title == null) throw new ArgumentNullException("Title");
        ////    if (Value == null) throw new ArgumentNullException("Value");

        ////    // Write the values
        ////    Console.Write("| ");
        ////    Console.Write(Title.PadLeft(20));
        ////    Console.Write(" : ");
        ////    Console.Write(Value.PadRight(consoleWidth - 27));
        ////    Console.Write(" |");
        //// }

        #endregion

        #region Console Section Drawing

        /// <summary>
        /// Draws the header on the console screen
        /// </summary>
        private static void DrawHeader()
        {
            // Set cursor at the start of the section for title
            Console.SetCursorPosition(0, 0);

            // Set Title area colors
            Console.BackgroundColor = ConsoleColor.DarkCyan;
            Console.ForegroundColor = ConsoleColor.White;

            // Draw Top Border
            WriteHeaderDivider();

            // Write out the app name and company
            WriteHeaderTextCenter(SystemGlobals.AssemblyInfo.Title);
            WriteHeaderTextCenter(SystemGlobals.AssemblyInfo.Copyright);

            // Draw middle border
            WriteHeaderDivider();

            // Draw versioning information
            WriteHeader2Column("Version", SystemGlobals.AssemblyInfo.AssemblyVersion.ToString(), "Edition", SystemGlobals.AssemblyInfo.Configuration);

            // Draw bottom border
            WriteHeaderDivider();

            // Reset colors
            Console.ResetColor();
        }

        /// <summary>
        /// Draws the header for the log table on the screen
        /// </summary>
        private static void DrawTableLogHeader()
        {
            int currentWidth = LOG_FIELDLENGTH_MESSAGE;

            // Set cursor at the start of the section for title
            try
            {
                Console.SetCursorPosition(0, ROW_LOGS_TITLE);
            }
            catch (IOException)
            {
            }

            // Set Title area colors
            Console.BackgroundColor = ConsoleColor.Black;
            Console.ForegroundColor = ConsoleColor.Gray;

            if (_consoleWidth > currentWidth)
            {
                Console.Write("TIME".PadRight(LOG_FIELDLENGTH_TIME));
                currentWidth += LOG_FIELDLENGTH_TIME;
            }

            if (_consoleWidth > currentWidth)
            {
                Console.Write("EVENT ID".PadRight(LOG_FIELDLENGTH_EVENTID));
                currentWidth += LOG_FIELDLENGTH_EVENTID;
            }

            if (_consoleWidth > currentWidth)
            {
                Console.Write("TITLE".PadRight(LOG_FIELDLENGTH_TITLE));
                currentWidth += LOG_FIELDLENGTH_TITLE;
            }

            if (_consoleWidth > currentWidth)
            {
                Console.Write("MESSAGE".PadRight(_consoleWidth - currentWidth));
            }

            Console.WriteLine();

            // Reset colors
            Console.ResetColor();
        }

        /// <summary>
        /// Draws the command line area of the screen
        /// </summary>
        /// <param name="command">Command to show in the CLI area</param>
        private static void DrawCommandSection(string command)
        {
            if (command == null)
            {
                throw new ArgumentNullException("command");
            }

            try
            {
                Console.SetCursorPosition(0, _consoleHeight - 4);
            }
            catch (IOException)
            {
            }

            Console.WriteLine(string.Empty.PadRight(_consoleWidth));

            try
            {
                Console.SetCursorPosition(0, _consoleHeight - 3);
            }
            catch (IOException)
            {
            }

            Console.Write(" > ");
            Console.Write(command.PadRight(_consoleWidth - 3));
            Console.ResetColor();
        }

        #endregion

        #region Process Input

        /// <summary>
        /// Method to process all key input from the console that contains a Control Modifier
        /// </summary>
        /// <param name="keyInfo">ConsoleKeyInfo with information about the key</param>
        private static void ProcessControlKeyInput(ConsoleKeyInfo keyInfo)
        {
            switch (keyInfo.Key)
            {
                case ConsoleKey.C:
                    Environment.Exit(0);
                    break;
            }
        }

        /// <summary>
        /// Method to process all key input from the console that does not contain a Modifier
        /// </summary>
        /// <param name="keyInfo">ConsoleKeyInfo with information about the key</param>
        private void ProcessNormalKeyInput(ConsoleKeyInfo keyInfo)
        {
            switch (keyInfo.Key)
            {
                #region DELETE Key

                // Delete the last character
                case ConsoleKey.Delete:

                    // Only do something if there is a command
                    if (!string.IsNullOrEmpty(_currentCommand))
                    {
                        if (_currentCursorIndex < _currentCommand.Length)
                        {
                            _currentCommand = _currentCommand.Remove(_currentCursorIndex, 1);

                            if (_currentCursorIndex >= _currentCommand.Length)
                            {
                                _currentCursorIndex = _currentCommand.Length;
                            }
                        }
                    }

                    break;

                #endregion

                #region BACKSPACE Key

                // Backspace the previous character
                case ConsoleKey.Backspace:

                    // Only do something if there is a command
                    if (!string.IsNullOrEmpty(_currentCommand) && _currentCursorIndex > 0)
                    {
                        if (_currentCursorIndex > _currentCommand.Length)
                        {
                            _currentCursorIndex = _currentCommand.Length;
                        }

                        _currentCommand = _currentCommand.Remove(_currentCursorIndex - 1, 1);

                        if (_currentCursorIndex >= _currentCommand.Length)
                        {
                            _currentCursorIndex = _currentCommand.Length;
                        }
                        else
                        {
                            _currentCursorIndex -= 1;
                            if (_currentCursorIndex < 0)
                            {
                                _currentCursorIndex = 0;
                            }
                        }
                    }

                    break;

                #endregion

                #region ENTER Key

                // Run Command(s)
                case ConsoleKey.Enter:
                    if (!string.IsNullOrEmpty(_currentCommand))
                    {
                        ProcessCommand(_currentCommand);

                        if (_recentCommands.Count > 30)
                        {
                            _recentCommands.Dequeue();
                        }

                        _recentCommands.Enqueue(_currentCommand);
                        _currentCommandIndex = _recentCommands.Count - 1;
                        _currentCursorIndex = 0;
                    }

                    _currentCommand = string.Empty;
                    break;

                #endregion

                #region ESCAPE Key

                // Escape allows the last command to be cleared
                case ConsoleKey.Escape:
                    _currentCommand = string.Empty;
                    _currentCursorIndex = 0;
                    break;

                #endregion

                #region ARROW-UP Key

                // Go Up through the recent command list
                case ConsoleKey.UpArrow:
                    if (_recentCommands.Count > 0)
                    {
                        _currentCommandIndex += 1;

                        if (_currentCommandIndex >= (_recentCommands.Count - 1))
                        {
                            _currentCommandIndex = _recentCommands.Count - 1;
                        }

                        _currentCommand = _recentCommands.ToArray()[_currentCommandIndex];
                        _currentCursorIndex = _currentCommand.Length - 1;
                    }

                    break;

                #endregion

                #region ARROW-DOWN Key

                // Go Down through the recent command list
                case ConsoleKey.DownArrow:
                    if (_recentCommands.Count > 0)
                    {
                        if (_currentCommandIndex <= 0)
                        {
                            _currentCommandIndex = 0;
                        }
                        else
                        {
                            _currentCommandIndex -= 1;
                        }

                        _currentCommand = _recentCommands.ToArray()[_currentCommandIndex];
                        _currentCursorIndex = _currentCommand.Length - 1;
                    }

                    break;

                #endregion

                #region ARROW-LEFT Key

                // Go left on the current command
                case ConsoleKey.LeftArrow:
                    if (_currentCommand != null)
                    {
                        if (_currentCommand.Length > 0)
                        {
                            if (_currentCursorIndex <= 0)
                            {
                                _currentCursorIndex = 0;
                            }
                            else
                            {
                                _currentCursorIndex -= 1;
                            }
                        }
                        else
                        {
                            _currentCursorIndex = 0;
                        }
                    }

                    break;

                #endregion

                #region ARROW-RIGHT Key

                // Go right on the current command
                case ConsoleKey.RightArrow:
                    if (_currentCommand != null)
                    {
                        if (_currentCommand.Length > 0)
                        {
                            if (_currentCursorIndex >= _currentCommand.Length)
                            {
                                _currentCursorIndex = _currentCommand.Length;
                            }
                            else
                            {
                                _currentCursorIndex += 1;
                            }
                        }
                        else
                        {
                            _currentCursorIndex = 0;
                        }
                    }

                    break;

                #endregion

                #region HOME Key

                // Go to the first index of the cursor
                case ConsoleKey.Home:
                    _currentCursorIndex = 0;
                    break;

                #endregion

                #region END Key

                // Go to the last index of the cursor
                case ConsoleKey.End:
                    if (_currentCommand != null)
                    {
                        _currentCursorIndex = _currentCommand.Length;
                    }

                    break;

                #endregion

                // All other characters
                default:
                    if (string.IsNullOrEmpty(_currentCommand))
                    {
                        _currentCommand += keyInfo.KeyChar;
                    }
                    else
                    {
                        _currentCommand = _currentCommand.Insert(_currentCursorIndex, keyInfo.KeyChar.ToString());
                    }

                    _currentCursorIndex += 1;
                    break;
            }
        }

        #endregion

        #region Manager Functions

        /// <summary>
        /// Loop method for managing the input from the console
        /// </summary>
        private void ConsoleReadManager()
        {
            // Declare local Variables
            ConsoleKeyInfo keyInfo;

            // Catch thread aborting
            try
            {
                Log.AddEntry("Thread Started", "Console Input Thread", LogPriorities.Verbose, TraceEventType.Verbose, (int)CoreLogCategories.ConsoleManager);

                // Infinite loop start for thread loop
                while (true)
                {
                    // Gets the next keypress
                    keyInfo = Console.ReadKey(true);

                    // See if there was a modifier
                    switch (keyInfo.Modifiers)
                    {
                        case ConsoleModifiers.Control: // CTRL + Commands
                            ProcessControlKeyInput(keyInfo);
                            break;
                        default: // Normal Commands
                            ProcessNormalKeyInput(keyInfo);
                            break;
                    }

                    // Tell console helper to schedule a command update
                    AddConsoleUpdate(ConsoleUpdateType.Command);
                }
            }
            catch (ThreadAbortException)
            {
                Log.AddEntry("Thread Stopping", "Console Input Thread", LogPriorities.Verbose, TraceEventType.Verbose, (int)CoreLogCategories.ConsoleManager);
            }
        }

        #endregion

        #region Command Line Helper Functions

        /// <summary>
        /// Gets a formatted command to display in the entry area of the screen
        /// </summary>
        /// <returns>Formatted command screen based on the current index of the cursor</returns>
        private string GetCommandForDisplay()
        {
            // Declare local variables
            int afterCursor = _currentCommand.Length - _currentCursorIndex;
            int beforeCursor = _currentCommand.Length - afterCursor;
            int viewStart = -1;
            int viewEnd = -1;
            string viewString = "{0}";
            int consoleHalf = (_consoleWidth / 2) - 6;

            // If we are in the middle of the command line with more than 42 characters
            // On each side then add ... to beginning and end
            if ((afterCursor > consoleHalf) && (beforeCursor > consoleHalf))
            {
                // Show ...{0}...
                viewStart = _currentCursorIndex - consoleHalf;
                viewEnd = _currentCursorIndex + consoleHalf;
                viewString = "...{0}...";
            }

            // If we have more than 90 characters and the command is at the very beginning add ... to end
            else if (((afterCursor > consoleHalf) && (beforeCursor <= consoleHalf)) && _currentCommand.Length > _consoleWidth - 6)
            {
                // Show {0}...
                viewStart = 0;
                viewEnd = (consoleHalf * 2) + 3;
                viewString = "{0}...";
            }

            // If we have more than 90 characters and the command is at the very end add ... to beginning
            else if (((afterCursor <= consoleHalf) && (beforeCursor > consoleHalf)) && _currentCommand.Length > _consoleWidth - 6)
            {
                // Show ...{0}
                viewStart = _currentCommand.Length - ((consoleHalf * 2) + 3);
                viewEnd = _currentCommand.Length;
                viewString = "...{0}";
            }

            // Default to 0 if the start is less than the beginning of _currentCommand
            if (viewStart < 0)
            {
                viewStart = 0;
            }

            // Default to last character if the end is more than the actual
            if (viewEnd > _currentCommand.Length || viewEnd == -1)
            {
                viewEnd = _currentCommand.Length;
            }

            // Generate string
            return string.Format(viewString, _currentCommand.Substring(viewStart, viewEnd - viewStart));
        }

        /// <summary>
        /// Gets the current display version of the cursor for command line input
        /// </summary>
        /// <returns>X position for the cursor on the screen</returns>
        private int GetCursorDisplayPosition()
        {
            // Declare local variables
            int afterCursor = _currentCommand.Length - _currentCursorIndex;
            int beforeCursor = _currentCommand.Length - afterCursor;
            int consoleHalf = (_consoleWidth / 2) - 6;

            // If the command is less than the display
            if ((_currentCommand.Length < _consoleWidth - 6) && (afterCursor > consoleHalf) && (beforeCursor > consoleHalf))
            {
                return _currentCursorIndex + 1;
            }

            // If the command is the same length as the display
            if ((_currentCommand.Length == _consoleWidth - 6) && (afterCursor > consoleHalf) && (beforeCursor > consoleHalf))
            {
                return (_consoleWidth / 2) - 2;
            }

            // Commands greater than the display
            if (_currentCommand.Length > _consoleWidth - 6)
            {
                if (beforeCursor > consoleHalf - 1)
                {
                    if (afterCursor < consoleHalf + 1)
                    {
                        return _consoleWidth - afterCursor - 6;
                    }

                    if (afterCursor >= consoleHalf + 1)
                    {
                        return (_consoleWidth / 2) - 3;
                    }
                }
            }

            // Default to the index
            return _currentCursorIndex;
        }

        #endregion

        /// <summary>
        /// Processes all queued up updates for the console
        /// </summary>
        private void UpdateConsole()
        {
            // If we are already updating then we want to return
            if (_isUpdating || !_initialized)
            {
                return;
            }

            // Hide cursor and lock the update from being called again
            try
            {
                Console.CursorVisible = false;
            }
            catch (IOException)
            {
            }

            _isUpdating = true;

            // Process all updates
            while (_updates.Count > 0)
            {
                ConsoleUpdateType update = _updates.Dequeue();

                // Update based on the type
                switch (update)
                {
                    case ConsoleUpdateType.Header:
                        DrawHeader();
                        break;

                    case ConsoleUpdateType.Content:
                        DrawTableLogHeader();
                        DrawTableLogContent();
                        break;

                    case ConsoleUpdateType.Command:
                        DrawCommandSection(GetCommandForDisplay());
                        break;
                }
            }

            // Reset the cursor position
            try
            {
                Console.SetCursorPosition(3 + GetCursorDisplayPosition(), _consoleHeight - 3);
            }
            catch (IOException)
            {
            }

            _isUpdating = false;
            try
            {
                Console.CursorVisible = true;
            }
            catch (IOException)
            {
            }
        }

        /// <summary>
        /// Draws the content for the log table on the screen
        /// </summary>
        private void DrawTableLogContent()
        {
            // Set cursor at the start of the section for title
            try
            {
                Console.SetCursorPosition(0, ROW_LOGS_TITLE + 1);
            }
            catch (IOException)
            {
            }

            // Set Title area colors
            Console.BackgroundColor = ConsoleColor.Black;

            for (int x = 0; x < _logCountMaximum; x++)
            {
                Console.Write(string.Empty.PadRight(_consoleWidth));
            }

            try
            {
                Console.SetCursorPosition(0, ROW_LOGS_TITLE + 1);
            }
            catch (IOException)
            {
            }

            lock (_eventLog)
            {
                foreach (LogMessage message in _eventLog)
                {
                    int currentWidth = LOG_FIELDLENGTH_MESSAGE;

                    switch (message.Severity)
                    {
                        default:
                            Console.ForegroundColor = ConsoleColor.White;
                            break;
                        case TraceEventType.Warning:
                        case TraceEventType.Resume:
                        case TraceEventType.Suspend:
                            Console.ForegroundColor = ConsoleColor.Yellow;
                            break;
                        case TraceEventType.Critical:
                        case TraceEventType.Error:
                        case TraceEventType.Stop:
                            Console.ForegroundColor = ConsoleColor.Red;
                            break;
                        case TraceEventType.Start:
                            Console.ForegroundColor = ConsoleColor.Green;
                            break;
                    }

                    if (_consoleWidth > currentWidth)
                    {
                        if (message.TimeStamp < DateTime.Today)
                        {
                            Console.Write(message.TimeStamp.ToLocalTime().ToString("DD.MM").PadRight(LOG_FIELDLENGTH_TIME));
                        }
                        else
                        {
                            Console.Write(message.TimeStamp.ToLocalTime().ToString("HH:mm").PadRight(LOG_FIELDLENGTH_TIME));
                        }

                        currentWidth += LOG_FIELDLENGTH_TIME;
                    }

                    if (_consoleWidth > currentWidth)
                    {
                        if (message.EventID != 0)
                        {
                            Console.Write(message.EventID.ToString().PadRight(LOG_FIELDLENGTH_EVENTID));
                        }
                        else
                        {
                            Console.Write(string.Empty.PadRight(LOG_FIELDLENGTH_EVENTID));
                        }

                        currentWidth += LOG_FIELDLENGTH_EVENTID;
                    }

                    if (_consoleWidth > currentWidth)
                    {
                        if (message.Title.Length > LOG_FIELDLENGTH_TITLE)
                        {
                            Console.Write(message.Title.Substring(0, LOG_FIELDLENGTH_TITLE - 3) + "...");
                        }
                        else
                        {
                            Console.Write(message.Title.PadRight(LOG_FIELDLENGTH_TITLE));
                        }

                        currentWidth += LOG_FIELDLENGTH_TITLE;
                    }

                    if (_consoleWidth > currentWidth)
                    {
                        if (message.Title.Length > _consoleWidth - currentWidth)
                        {
                            Console.Write(message.Description.Substring(0, _consoleWidth - currentWidth - 3) + "...");
                        }
                        else
                        {
                            Console.Write(message.Description.PadRight(_consoleWidth - currentWidth));
                        }
                    }

                    Console.WriteLine();
                }
            }

            // Reset colors
            Console.ResetColor();
        }

        /// <summary>
        /// Takes the input from a person hitting the enter key after typing a command and processes it
        /// </summary>
        /// <param name="command">Command to process</param>
        private void ProcessCommand(string command)
        {
            Log.AddEntry("Process Command", command, LogPriorities.Verbose, TraceEventType.Verbose, (int)CoreLogCategories.ConsoleManager);

            // Split based upon the space.
            List<string> split = ArgumentHelper.SplitArguments(command);

            if (split.Count <= 0)
            {
                return;
            }

            // Is the command at least 2 part?
            if (split.Count > 1)
            {
                // We want to look through all our handlers to see if there is a
                // matching root command we can process
                ICommandLineInterfaceHandler handler = null;

                lock (_syncCLI)
                {
                    if (_cliHandlers.ContainsKey(split[0].ToLower()))
                    {
                        handler = _cliHandlers[split[0].ToLower()];
                    }
                }

                // If we found a handler then we will call it with the remaining arguments in a list
                if (handler != null)
                {
                    Log.AddEntry("Handling Command", string.Format("Handler: {0}, Command: {1}", handler.GetType().Name, command), LogPriorities.Debug, TraceEventType.Verbose, (int)CoreLogCategories.ConsoleManager);
                    handler.HandleCommand(split.GetRange(1, split.Count - 1));
                }
                else
                {
                    ProcessInvalidCommand(command);
                }
            }
            else
            {
                ProcessInvalidCommand(command);
            }
        }
    }
}